package com.xpec.c4.game.datamanager.agent;

import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.ms.game.itembox.ChangeItem;
import com.ms.game.itembox.ChangeItemLister;
import com.ms.game.itembox.CrystalChangeWay;
import com.ms.game.itembox.GameDropItemInfo;
import com.ms.game.itembox.GameDropItemParam;
import com.ms.game.itembox.Item;
import com.ms.game.itembox.ItemChangeParam;
import com.ms.game.itembox.ItemChangeWay;
import com.ms.game.itembox.ItemSimpleData;
import com.ms.game.itembox.RollItemSys;
import com.ms.game.itembox.container.ItemBasic;
import com.ms.game.itembox.container.ItemCrystalCard;
import com.ms.game.itembox.message.SSimpleItem;
import com.ms.player.Player;
import com.ms.player.PlayerAttributeType;
import com.ms.player.PlayerUserType;
import com.ms.player.UpdateAttribute;
import com.ms.player.UpdateAttributeList;
import com.ms.player.UpdateItemList;
import com.ms.player.message.SUpdatePlayerZoneDataByAttributeList;
import com.ms.service.error.ErrorCode;
import com.ms.service.module.analysisgroup.AnalysisGroupProcessor;
import com.ms.service.module.analysisgroup.CoinExpenditureWay;
import com.ms.service.module.zone.BasicZone;
import com.ms.service.module.zone.module.BasicZoneModule;
import com.xpec.c4.db.dao.PlayerDAO;
import com.xpec.c4.game.character.PlayerManager;
import com.xpec.c4.game.common.Helper;
import com.xpec.c4.game.datamanager.BaseDataInfo;
import com.xpec.c4.game.datamanager.DataManager;
import com.xpec.c4.game.datamanager.ResultNo;
import com.xpec.c4.game.datamanager.agent.money.ChangeCoin;
import com.xpec.c4.game.datamanager.agent.money.ChangeCoinLister;
import com.xpec.c4.game.datamanager.agent.money.CoinType;
import com.xpec.c4.game.datamanager.agent.money.MoneyChangeParam;
import com.xpec.c4.game.datamanager.agent.money.MoneyChangeWay;
import com.xpec.c4.game.datamanager.agent.moneyItem.MoneyItemChangeParam;
import com.xpec.c4.game.time.TimeSys;
import com.xpec.c4.game.utility.GUID;
import com.xpec.c4.game.utility.GameUtility;
import com.xpec.c4.message.MessageTypes;
import com.xpec.c4.service.message.MessageAction;
import com.xpec.c4.service.module.BasicServiceModule;
import com.xpec.c4.service.module.ModuleName;
import com.xpec.c4.service.module.ModuleServer;
import com.xpec.c4.service.module.ServerProperties;
import com.xpec.c4.service.module.common.ItemInfoManager;
import com.xpec.c4.service.module.common.PlayerCommonInfoManager;
import com.xpec.c4.service.module.datacenter.player.SaveCharacterDaliyLogAsync;
import com.xpec.c4.service.module.datacenter.player.UpdatePlayerMoneyAsync;

/**
 * 
 * @author Mike_Tung
 *
 */
public class PlayerDataAgent extends DataManager<PlayerData>{
	
	private static Log log = LogFactory.getLog(PlayerDataAgent.class);

	// test
	// private long startTime = System.nanoTime();
	
	/**
	 * 
	 * @param module
	 */
	public PlayerDataAgent(BasicServiceModule module) {
		super(module);	
	}

	/**
	 * 
	 * @param guid
	 * @return
	 */
	public Player getPlayerByGuid(GUID guid){
		if(this.getData(guid) != null){
			return (Player)this.getData(guid).getData();
		}
		return null;
	}
	
	/**
	 * 取得指定的 zone<br>
	 * 方便在 dc 返回的 callback 中呼叫
	 * 
	 * @param moduleId
	 *            module id
	 * @return zone
	 */
	public BasicZone getBasicZone(String moduleId) {
		if (moduleId == null) {
			return null;
		}

		Object object = ModuleServer.getInstance().getBasicServiceModule(moduleId);
		if (object instanceof BasicZoneModule) {
			BasicZoneModule module = (BasicZoneModule) object;
			BasicZone basicZone = module.getZone();

			return basicZone;
		}

		return null;
	}
	
	/**
	 * 從 zone 中取得指定的 player<br>
	 * 方便在 dc 返回的 callback 中呼叫
	 * 
	 * @param moduleId
	 *            module id
	 * @param playerGuid
	 *            player guid
	 * @return player data
	 */
	public Player getPlayerFromZone(String moduleId, GUID playerGuid) {
		if (moduleId == null || playerGuid == null) {
			return null;
		}

		BasicZone basicZone = getBasicZone(moduleId);
		if (basicZone == null) {
			return null;
		}

		Player player = basicZone.getPlayer(playerGuid);

		return player;
	}

	/**
	 * 透過 DataCenter 更新 player 資料到 DB
	 * 
	 * @param player
	 *            player data
	 * @param updateAttrList
	 *            要異動的屬性清單, 參考 {@link PlayerData}
	 * @param callback
	 *            callback function name
	 * @param userData
	 *            user custom parameter
	 * @return true 成功<br>
	 *         false 失敗
	 */
	// TODO kevin 呼叫這個的改成呼叫 updatePlayerDataToDC
	public boolean updatePlayerDataToDC(Player player, UpdateAttributeList updateAttrList, String callback,
			Object... userData) {
		if (player.isDcQuery()) {
			return false;
		}

		PlayerData data = new PlayerData();

		data.setKey(player.getGuid());
		data.setPlayer(player);
		data.setUpdateAttrList(updateAttrList);
		data.setMessageType(MessageTypes.UPDATE_PLAYER_INFO);

		// 產生預處理callback需要的method
		Object classOfPreProcMethod = (PlayerDataAgent) this;
		Method preProcMth = getCallbackMethod(classOfPreProcMethod, "updatePlayerDataToDCPreProcCallback");

		// 產生callback需要的method
		Method mth;
		if (callback != null) {
			mth = getCallbackMethod(this, callback);
		} else {
			mth = null;
		}

		player.setDcQuery(true);
		return super.dataRequest(this, classOfPreProcMethod, preProcMth, this, mth, data, userData);
	}
	
	/**
	 * 更新玩家資料到 dc 返回後的預處理 callback<p>
	 * set from {@link #updatePlayerDataToDC(Player, String, Object...)}
	 * @param obj
	 * @param userObject
	 */
	public void updatePlayerDataToDCPreProcCallback(BaseDataInfo obj, Object... userObject) {
		PlayerData data = (PlayerData) obj;

		Player player = data.getPlayer();
		if (player == null) {
			return;
		}

		Player playerOnZone = getPlayerFromZone(player.getZoneId(), player.getGuid());
		if (playerOnZone == null) {
			return;
		}

		playerOnZone.setDcQuery(false);
	}

	/**
	 * 透過 DataCenter 變更角色金錢
	 * <p>
	 * callback function 格式參考
	 * {@link #updatePlayerMoneyToDcPreProcCallback(BaseDataInfo, Object...)}
	 * <p>
	 * 確認執行結果的方法請參考 {@link PlayerDAO#changeMoneyVerify(MoneyChangeParam)}
	 * 
	 * @param orderId
	 *            交易id
	 * @param player
	 *            角色資料
	 * @param coinLister
	 *            同時要異動的幣別清單
	 * @param changeWay
	 *            金錢變更行為類型
	 * @param jpMoney
	 *            有中 jp 的話填入 jp 金額, 沒中就填 {@link MoneyChangeParam#NO_JACKPOT}
	 * @param isOfflineBet
	 *            是否為離線押注
	 * @param rollItemsInGame
	 *            遊戲中獲得道具的資料
	 * @param gameId
	 *            遊戲id SGJS, HALLOWEEN, MGDH ....
	 * @param gameName
	 *            遊戲中文名稱
	 * @param gameHallId
	 *            遊戲館 id
	 * @param gameHallName
	 *            遊戲館名稱
	 * @param classOfCallback
	 *            class instance of callback function
	 * @param callback
	 *            callback function name
	 * @param userData
	 *            user custom parameter
	 * @return true 成功<br>
	 *         false 失敗
	 * @see MoneyChangeParam
	 */
	public boolean updatePlayerMoneyToDC(GUID orderId, Player player, ChangeCoinLister coinLister,
			MoneyChangeWay changeWay, BigDecimal jpMoney, boolean isOfflineBet,
			ArrayList<ItemSimpleData> rollItemsInGame, 
			ArrayList<ItemSimpleData> useItemsInGame,
			BasicServiceModule currentModule,
			String gameId, String gameName, String gameHallId, String gameHallName, BigDecimal gainRate,
			Object classOfCallback, String callback,
			Object... userData) {
		if (player == null) {
			if (log.isErrorEnabled()) {
				log.error("updatePlayerMoneyToDC fail, player = null");
			}
			return false;
		}

		if (player.isDcQuery()) {
			if (log.isErrorEnabled()) {
				log.error("updatePlayerMoneyToDC fail, player.isDcQuery() = true | playerID = " + player.getGuid().toString() + " | playerName = " + player.getNameWithNumber());
			}
			return false;
		}

		if (orderId == null) {
			if (log.isErrorEnabled()) {
				log.error("updatePlayerMoneyToDC fail, orderId = null | playerID = " + player.getGuid().toString() + " | playerName = " + player.getNameWithNumber());
			}
			return false;
		}
		
		if(log.isInfoEnabled() && ServerProperties.isSaveConsoleLog(player.getUserType()))
		{
			log.info("<<updatePlayerMoneyToDC>> Befor Update Player(" +player.toString()+ ")    Gold = " + player.getGoldCoin() + "  Lucky = " + player.getLuckyCoin() + "  Trial = " + player.getTrialCoin() );
		}

		if(jpMoney == null){
			jpMoney = BigDecimal.ZERO;
		}
		
		MoneyChangeParam param = new MoneyChangeParam();
		param.setJpScore(jpMoney);
		
		if(gainRate != null && Helper.isGreaterThanZero(gainRate)){
			param.setGainRate(gainRate);	
		}
		
		// 掉落道具資訊 and 使用道具  填入 gameDropItemParam
		GameDropItemParam gameDropItemParam = null;
		if (changeWay.isDailyScore() || changeWay.isDailyBet()) {
			if (GameUtility.getArrayListSize(rollItemsInGame) > 0) {
				gameDropItemParam = new GameDropItemParam();
				for (ItemSimpleData simpleData : rollItemsInGame) {
					if (simpleData.isCrystal()) {
						gameDropItemParam.setDropCrystal(simpleData.getCount());
					} else {
						gameDropItemParam.addItem(simpleData.getItemid(), simpleData.getCount(),simpleData.getItemSn() ,player);
					}
				}
			}
			
			if (GameUtility.getArrayListSize(useItemsInGame) > 0) {
				gameDropItemParam = new GameDropItemParam();
				for (ItemSimpleData simpleData : useItemsInGame) {
					gameDropItemParam.addItem(simpleData.getItemid(), simpleData.getCount(), simpleData.getItemSn(), player);
				}
				//設定使用道具
				gameDropItemParam.setUseItem(true);
			}
		}

		// 有沒有指定幣別
		boolean haveCoin = true;
		if (coinLister == null || coinLister.getSize() == 0) {
			haveCoin = false;
		}
		
		// 是否有遊戲中掉落的道具 或是 使用道具
		boolean haveDropItems = true;
		if (gameDropItemParam == null || !gameDropItemParam.isHaveReward()) {
			haveDropItems = false;
		}

		// 如果是遊戲中得分
		if (changeWay.isDailyScore()) {
			// 沒有指定幣別 && 沒有掉道具
			if (!haveCoin && !haveDropItems) {
				if (log.isErrorEnabled()) {
					log.error("updatePlayerMoneyToDC isDailyScore, no coin & item | playerID = " + player.getGuid().toString() + " | playerName = " + player.getNameWithNumber());
				}
				return false;
			}
		} else {
			// 如果沒有使用道具 又 沒有幣別就結束
			if (!haveCoin) {
				if (log.isErrorEnabled()) {
					log.error("updatePlayerMoneyToDC fail, no coin  | playerID = " + player.getGuid().toString() + " | playerName = " + player.getNameWithNumber());
				}
				return false;
			}
		}

		// 設定遊戲中獲得道具資訊
		if (gameDropItemParam != null) {
			param.setDropItemParam(gameDropItemParam);
		}

		// 掃一遍所有的幣別
		Iterator<Map.Entry<CoinType, ChangeCoin>> it = coinLister.getListIterator();
		ChangeCoin coinInfo = coinLister.getNextCoin(it);
		while (coinInfo != null) {
			BigDecimal currentValue = player.getCoin(coinInfo.getCoinType());
			if (Helper.isEqual(currentValue, new BigDecimal(CoinType.UNKNOW_TYPE.getId()))) {
				if (log.isErrorEnabled()) {
					log.error("updatePlayerMoneyToDC fail, unknow coin type | playerID = " + player.getGuid().toString() + " | playerName = " + player.getNameWithNumber());
				}
				return false;
			}

			// 填入指定幣別的目前值
			coinInfo.setCurrentValue(currentValue);

			coinInfo = coinLister.getNextCoin(it);
		}

		param.setOrderId(orderId);
		param.setChangeWay(changeWay);
		param.setChangeCoinLister(coinLister);
		param.setGameId(gameId);
		param.setGameName(gameName);
		param.setGameHallId(gameHallId);
		param.setGameHallName(gameHallName);
		param.setAccountId(player.getAccountId());
		param.setPlayerId(player.getGuid());
		param.setPlayerName(player.getNameWithNumber());
		param.setUserType(player.getUserType());
		param.setSrcModuleId(player.getZoneId());
		param.setVenderId(player.getVenderId());
		param.setSubVenderId(player.getSubVenderId());
		
		// 給analysis module判斷即時離線SPIN總投注用的
		param.setOfflineSpin(isOfflineBet);
		//設定發生的時間
		param.setChangeTime(TimeSys.currentTimeMills());

		BaseDataInfo data = new BaseDataInfo();

		data.setKey(param.getPlayerId());
		data.setData(param);
		data.setMessageType(MessageTypes.UPDATE_PLAYER_MONEY);

		// 產生預處理callback需要的method
		Object classOfPreProcMethod = (PlayerDataAgent) this;
		Method preProcMth;
		preProcMth = getCallbackMethod(classOfPreProcMethod, "updatePlayerMoneyToDcPreProcCallback");

		// 產生callback需要的method
		Method mth;
		if (callback != null) {
			mth = getCallbackMethod(classOfCallback, callback);
		} else {
			mth = null;
		}

		player.setDcQuery(true);
		
		// 執行金錢異動
		PlayerManager.getInstance().updatePlayerMoneyToDB(player, currentModule, param);
		data.setResultCode(ErrorCode.SUCCESS);
		
		// 記錄玩家基本資訊的每日log
		SaveCharacterDaliyLogAsync dailyLogAsync = new SaveCharacterDaliyLogAsync();
		dailyLogAsync.setParam(param);
		dailyLogAsync.key = player.getGuid();
		currentModule.addAsyncCommand(dailyLogAsync);
		
		// 記錄金錢異動 log
		UpdatePlayerMoneyAsync.saveUpdatePlayerMoneyLog(currentModule, param);
		
		dataProcessCallback(classOfPreProcMethod, preProcMth, data, userData);
		dataProcessCallback(classOfCallback, mth, data, userData);
		return true;
	}

	/**
	 * 透過 DataCenter 變更角色道具
	 * <p>
	 * callback function 格式參考
	 * {@link #updatePlayerItemToDcPreProcCallback(BaseDataInfo, Object...)}
	 * <p>
	 * 確認執行結果的方法請參考 {@link PlayerDAO#changeItemVerify(ItemChangeParam)}
	 * 
	 * @param orderId
	 *            交易id
	 * @param player
	 *            角色資料
	 * @param targetId
	 *            目標角色的 guid
	 * @param itemLister
	 *            同時要異動的幣別清單
	 * @param changeWay
	 *            金錢變更行為類型
	 * @param gameHallId
	 *            遊戲館 id
	 * @param gameHallName
	 *            遊戲館名稱
	 * @param classOfCallback
	 *            class instance of callback function
	 * @param expChangeValue
	 * 			      要加的經驗值得值 要>=0
	 * @param callback
	 *            callback function name
	 * @param userData
	 *            user custom parameter
	 * @return true 成功<br>
	 *         false 失敗
	 * @see MoneyChangeParam
	 */
	public boolean updatePlayerItemToDC(
			GUID orderId,
			Player player,
			GUID targetId,
			ChangeItemLister itemLister,
			ItemChangeWay changeWay,
			String gameHallId,
			String gameHallName,
			BigDecimal gainRate,
			BigDecimal expChangeValue, 
			Object classOfCallback,	String callback,
			Object... userData) {
		if (player == null) {
			return false;
		}

		if (player.isDcQuery()) {
			return false;
		}

		if (orderId == null) {
			return false;
		}
		
		 //經驗不能是負的
		if(expChangeValue == null || Helper.isMinus(expChangeValue)) {
			return false;
		}

		ItemChangeParam param = new ItemChangeParam();

		// 一個道具也沒有
		if (itemLister == null || itemLister.getSize() == 0) {
			return false;
		}
		
		if(gainRate != null && Helper.isGreaterThanZero(gainRate)){
			param.setGainRate(gainRate);
		}
		
		//是否增加水晶
		boolean isAddCrystal = false;
		//角色身上水晶
		int playerCrystalCount = player.getCrystal();
		//水晶數量
		int crystalChangeCount = 0;
		//水晶卡資訊
		ArrayList<Item> crystalCardList = new ArrayList<Item>();
		
		// 掃一遍所有的道具
		Iterator<Map.Entry<String, ChangeItem>> it = itemLister.getListIterator();
		ChangeItem itemInfo = itemLister.getNextItem(it);
		while (itemInfo != null) {

			ItemBasic item = null;
			// 數據表檢查物品是否存在
			if (changeWay.isAdd()) {
				item = ItemInfoManager.getInstance().getItem(itemInfo.getItemId());
				if (item == null) {
					if(log.isErrorEnabled()){
						log.error("Item " + itemInfo.getItemId() + " Not Found From Bin!");
					}
					return false;
				}
			}

			// 取得物品目前數量
			int currentCount = 0;
			if (changeWay.isAdd()) {
				// 增加物品
				if (item.isCrystalCard()) { // 如果物品是水晶
					isAddCrystal = true;
					//組成水晶道具卡
					ItemCrystalCard itemCrystalCard = (ItemCrystalCard) item;
					Item crystal = new Item();
					crystal.setItemId(itemCrystalCard.getItemId());
					crystal.setBasicItemId(itemCrystalCard.getDefaultItemId());
					crystal.setCount(itemInfo.getChangeCount());
					// 加到水晶卡資訊中
					crystalCardList.add(crystal);

					crystalChangeCount = crystalChangeCount + itemCrystalCard.getCrystalCount() * itemInfo.getChangeCount();
					currentCount = playerCrystalCount;
				} else {
					currentCount = player.getItemBox().getItemCountById(itemInfo.getItemId());
				}
			} else {
				// 減少物品
				// 取得指定的物品 id
				Item itemOnPlayer = player.getItemBox().getItem(itemInfo.getItemSn());
				if (itemOnPlayer == null) {
					return false;
				}
				itemInfo.setItemId(itemOnPlayer.getItemId());

				currentCount = player.getItemBox().getItemCount(itemInfo.getItemSn());
			}

			// 填入指定道具的目前值
			itemInfo.setCurrentCount(currentCount);

			itemInfo = itemLister.getNextItem(it);
		}

		// 目標角色的 name
		String targetName = null;
		if (targetId != null) {
			targetName = PlayerCommonInfoManager.getInstance().getPlayerNameByGuid(targetId);
		}
		
		CrystalChangeWay crystalChangeWay = null;
		if(isAddCrystal){
			//水晶的
			switch (changeWay) {
			case ADD_COMMAND:
				crystalChangeWay = CrystalChangeWay.ADD_COMMAND_SET;
				break;
			case ADD_IN_GAME:
				crystalChangeWay = CrystalChangeWay.ADD_RECV_FROM_GAME;
				break;
			case ADD_USE_BONUS_PACK:
				crystalChangeWay = CrystalChangeWay.ADD_RECV_FROM_BONUS_PACK;
				break;
			case ADD_MISSION_REWARD:
				crystalChangeWay = CrystalChangeWay.ADD_RECV_FROM_MISSION_REWARD;
				break;
			case ADD_OPEN_LEVEL_BOX:
				crystalChangeWay = CrystalChangeWay.ADD_RECV_FROM_LEVEL_BOX;
				break;
			case ADD_OPEN_GIFT_BOX:
				crystalChangeWay = CrystalChangeWay.ADD_RECV_FROM_GIFT_BOX;
				break;
			case ADD_BY_GMTOOL:
				crystalChangeWay = CrystalChangeWay.ADD_RECV_FROM_GMTOOL;
				break;
			case ADD_IN_FNBO:
				crystalChangeWay = CrystalChangeWay.ADD_RECV_FROM_FNBO;
			break;
			default:
				if (log.isErrorEnabled()) {
					log.error("Crystal Setting Error!");
				}
				return false;
			}
		}
	
		param.setOrderId(orderId);
		param.setChangeWay(changeWay);
		param.setChangeItemLister(itemLister);
		param.setGameHallId(gameHallId);
		param.setGameHallName(gameHallName);
		param.setAccountId(player.getAccountId());
		param.setPlayerId(player.getGuid());
		param.setPlayerName(player.getNameWithNumber());
		param.setPlayerLevel(player.getLevel());
		param.setPlayerVipLevel(player.getVip());
		param.setLastLevelBoxUseLevel(player.getLastLevelBoxUseLevel());
		param.setUserType(player.getUserType());
		param.setTargetId(targetId);
		param.setTargetName(targetName);
		param.setSrcModuleId(player.getZoneId());
		param.setCrystalChangeWay(crystalChangeWay);
		param.setAddCrystal(isAddCrystal);
		param.setCrystalBeforeChange(playerCrystalCount);
		param.setCrystalChangeValue(crystalChangeCount);
		param.setCrystalAfterChange(playerCrystalCount + crystalChangeCount);
		param.setCrystalCardList(crystalCardList);
		param.setExpChangeValue(expChangeValue);

		BaseDataInfo data = new BaseDataInfo();

		data.setKey(param.getPlayerId());
		data.setData(param);
		data.setMessageType(MessageTypes.UPDATE_PLAYER_ITEM);

		// 產生預處理callback需要的method
		Object classOfPreProcMethod = (PlayerDataAgent) this;
		Method preProcMth;
		preProcMth = getCallbackMethod(classOfPreProcMethod, "updatePlayerItemToDcPreProcCallback");

		// 產生callback需要的method
		Method mth;
		if (callback != null) {
			mth = getCallbackMethod(classOfCallback, callback);
		} else {
			mth = null;
		}

		player.setDcQuery(true);
		return super.dataRequest(this, classOfPreProcMethod, preProcMth, classOfCallback, mth, data, userData);
	}
	
	/**
	 * 透過 DataCenter 變更角色金錢
	 * <p>
	 * callback function 格式參考
	 * {@link #updatePlayerMoneyItemToDcPreProcCallback(BaseDataInfo, Object...)}
	 * <p>
	 * 確認執行結果的方法請參考 {@link PlayerDAO#changeMoneyVerify(MoneyChangeParam)}
	 * {@link PlayerDAO#changeItemVerify(ItemChangeParam)}
	 * 
	 * @param orderId
	 *            交易id
	 * @param player
	 *            角色資料
	 * @param coinLister
	 *            同時要異動的幣別清單
	 * @param changeWay
	 *            金錢變更行為類型
	 * @param updatedAttrList
	 * 			  要變更的角色屬性清單
	 * @param gameHallId
	 *            遊戲館 id
	 * @param gameHallName
	 *            遊戲館名稱
	 * @param classOfCallback
	 *            class instance of callback function
	 * @param callback
	 *            callback function name
	 * @param userData
	 *            user custom parameter
	 * @return true 成功<br>
	 *         false 失敗
	 * @see MoneyChangeParam
	 */
	public boolean updatePlayerMoneyAndItemToDC(GUID orderId, Player player, ChangeCoinLister coinLister,
			MoneyChangeWay goldChangeWay, ChangeItemLister itemLister, ItemChangeWay itemChangeWay,
			ArrayList<UpdateAttribute> updatedAttrList , String gameHallId, String gameHallName , 
			Object classOfCallback , String callback, Object... userData) {


		BaseDataInfo data = baseDataInfoMakerForUpdatePlayerMoneyAndItemToDC(orderId, player, coinLister, goldChangeWay,
				itemLister, itemChangeWay,updatedAttrList, gameHallId, gameHallName);
		
		if(data == null){
			return false;
		}
		
		// 產生預處理callback需要的method
		Object classOfPreProcMethod = (PlayerDataAgent) this;
		Method preProcMth;
		preProcMth = getCallbackMethod(classOfPreProcMethod, "updatePlayerMoneyItemToDcPreProcCallback");

		// 產生callback需要的method
		Method mth;
		if (callback != null) {
			mth = getCallbackMethod(classOfCallback, callback);
		} else {
			mth = null;
		}

		player.setDcQuery(true);
		return super.dataRequest(this, classOfPreProcMethod, preProcMth, classOfCallback, mth, data, userData);
	}
	
	public static BaseDataInfo  baseDataInfoMakerForUpdatePlayerMoneyAndItemToDC(GUID orderId, Player player, ChangeCoinLister coinLister,
			MoneyChangeWay goldChangeWay, ChangeItemLister itemLister, ItemChangeWay itemChangeWay,
			ArrayList<UpdateAttribute> updatedAttrList , String gameHallId, String gameHallName){

		if (player == null) {
			return null;
		}

		if (player.isDcQuery()) {
			return null;
		}

		if (orderId == null) {
			return null;
		}
		
		MoneyItemChangeParam params = new MoneyItemChangeParam();

		MoneyChangeParam MoneyParam = params.getMoneyParam();

		ItemChangeParam ItemParam = params.getItemParam();

		// 一種幣別也沒有 一個道具也沒有
		if (coinLister == null || itemLister == null) {
			return null;
		}

		// 一個數量也沒有
		if (coinLister.getSize() == 0 && itemLister.getSize() == 0) {
			return null;
		}

		// 掃一遍所有的幣別
		Iterator<Map.Entry<CoinType, ChangeCoin>> coinit = coinLister.getListIterator();
		ChangeCoin coinInfo = coinLister.getNextCoin(coinit);
		while (coinInfo != null) {
			BigDecimal currentValue = player.getCoin(coinInfo.getCoinType());
			if (Helper.isEqual(currentValue, new BigDecimal(CoinType.UNKNOW_TYPE.getId()))) {
				return null;
			}

			// 填入指定幣別的目前值
			coinInfo.setCurrentValue(currentValue);

			coinInfo = coinLister.getNextCoin(coinit);
		}

		// 掃一遍所有的道具
		Iterator<Map.Entry<String, ChangeItem>> itemit = itemLister.getListIterator();
		ChangeItem itemInfo = itemLister.getNextItem(itemit);
		while (itemInfo != null) {
			// 數據表檢查道具是否存在
			if (itemChangeWay.isAdd()) {
				ItemBasic item = ItemInfoManager.getInstance().getItem(itemInfo.getItemId());
				if (item == null) {
					return null;
				}
			}

			// 取得物品目前數量
			int currentCount = 0;
			if (itemChangeWay.isAdd()) {
				// 增加物品
				currentCount = player.getItemBox().getItemCountById(itemInfo.getItemId());
			} else {
				// 減少物品

				// 取得指定的物品 id
				Item itemOnPlayer = player.getItemBox().getItem(itemInfo.getItemSn());
				if (itemOnPlayer == null) {
					return null;
				}
				itemInfo.setItemId(itemOnPlayer.getItemId());

				currentCount = player.getItemBox().getItemCount(itemInfo.getItemSn());
			}

			// 填入指定道具的目前值
			itemInfo.setCurrentCount(currentCount);

			itemInfo = itemLister.getNextItem(itemit);
		}

		// money填值
		MoneyParam.setOrderId(orderId);
		MoneyParam.setChangeWay(goldChangeWay);
		MoneyParam.setChangeCoinLister(coinLister);
		MoneyParam.setGameHallId(gameHallId);
		MoneyParam.setGameHallName(gameHallName);
		MoneyParam.setAccountId(player.getAccountId());
		MoneyParam.setPlayerId(player.getGuid());
		MoneyParam.setPlayerName(player.getNameWithNumber());
		MoneyParam.setUserType(player.getUserType());
		MoneyParam.setSrcModuleId(player.getZoneId()); 
		MoneyParam.setChangeTime(TimeSys.currentTimeMills());

		// 道具填值
		ItemParam.setOrderId(orderId);
		ItemParam.setChangeWay(itemChangeWay);
		ItemParam.setChangeItemLister(itemLister);
		ItemParam.setGameHallId(gameHallId);
		ItemParam.setGameHallName(gameHallName);
		ItemParam.setAccountId(player.getAccountId());
		ItemParam.setPlayerId(player.getGuid());
		ItemParam.setPlayerName(player.getNameWithNumber());
		ItemParam.setUserType(player.getUserType());
		ItemParam.setSrcModuleId(player.getZoneId());
		
		if (updatedAttrList != null) {
			params.setUpdatedAttrList(updatedAttrList);
		}

		BaseDataInfo data = new BaseDataInfo();

		params.setAccountId(player.getAccountId());
		params.setPlayerId(player.getGuid());
		params.setPlayerName(player.getNameWithNumber());
		params.setSrcModuleId(player.getZoneId());
		params.setOrderId(orderId);

		data.setKey(player.getGuid());
		data.setData(params);
		data.setMessageType(MessageTypes.UPDATE_PLAYER_MONEY_ITEM);

		return data;
	} 
	
	/**
	 * 透過 DataCenter 更新 player 資料到 DB , Call Back To Anywhere
	 * 
	 * @param player
	 *            player data
	 * @param updateAttrList
	 *            要異動的屬性清單, 參考 {@link PlayerData}
	 * @param callback
	 *            callback function name
	 * @param userData
	 *            user custom parameter
	 * @return true 成功<br>
	 *         false 失敗
	 */
	public boolean updatePlayerDataToDC2(Player player, UpdateAttributeList updateAttrList, Object target,
			String callback, Object... userData) {
		if (player.isDcQuery()) {
			return false;
		}
		
		
		PlayerData data = new PlayerData();

		data.setKey(player.getGuid());
		data.setPlayer(player);
		data.setUpdateAttrList(updateAttrList);
		data.setMessageType(MessageTypes.UPDATE_PLAYER_INFO);

		// 產生預處理callback需要的method
		Object classOfPreProcMethod = (PlayerDataAgent) this;
		Method preProcMth = getCallbackMethod(classOfPreProcMethod, "updatePlayerDataToDCPreProcCallback");

		// 產生callback需要的method
		Method mth;
		if (callback != null) {
			mth = getCallbackMethod(target , callback);
		} else {
			mth = null;
		}

		player.setDcQuery(true);
		return super.dataRequest(this, classOfPreProcMethod, preProcMth, target, mth, data, userData);
	}	

	/**
	 * 向DataCenter獲取指定Player数据<br>
	 * 
	 * @param guid
	 */
	public boolean getPlayerDataByGuidFromDCOnlyForRead(GUID guid,
			boolean cacheInLocal  , String callback, Object... userData) {
		if (guid == null) {
			if (log.isErrorEnabled()) {
				GameUtility.logErrorException(log, "guid = null");
			}
		}

		PlayerData data = new PlayerData();
		
		data.setKey(guid);

		data.setMessageType(MessageTypes.QUERY_PLAYER_INFO_BY_GUID);		
		
		// 產生callback需要的method
		Method mth;
		if(callback != null){
			mth = getCallbackMethod(this, callback);
		}else{
			mth = null;
		}

		return super.dataRequest(this, null, null, this, mth, data, userData);
	}
	
	
	/**
	 * 向DataCenter獲取指定Player数据 ,  Call Back To Anywhere<br>
	 * 
	 * @param guid
	 */
	public boolean getPlayerDataByGuidFromDCOnlyForRead(GUID guid,
			boolean cacheInLocal ,Object target , String callback, Object... userData) {
		if (guid == null) {
			if (log.isErrorEnabled()) {
				GameUtility.logErrorException(log, "guid = null");
			}
		}

		PlayerData data = new PlayerData();
		
		data.setKey(guid);

		data.setMessageType(MessageTypes.QUERY_PLAYER_INFO_BY_GUID);		
		
		// 產生callback需要的method
		Method mth;
		if(callback != null){
			mth = getCallbackMethod(target , callback);
		}else{
			mth = null;
		}

		return super.dataRequest(this, null, null, target, mth, data, userData);
	}
	
	/**
	 * callback
	 */
	public void queryPlayerDataCallback(BaseDataInfo obj, Object... userObject){
		
		switch(obj.getResultCode()){
		
		case ResultNo.OK:
		{
			this.putData(obj.getKey(), obj.getHandle(), (PlayerData) obj);
			break;
		}
			
		case ResultNo.ACCESS_DENY:
			if(log.isErrorEnabled()){
				log.error("Access deny  id: " + obj.getKey());
			}
			break;
			
		case ResultNo.FAILED:
			if(log.isErrorEnabled()){
				log.error("Query failed  id: " + obj.getKey());
			}
			break;
			
		case ResultNo.DATA_NOT_FOUND:
			if(log.isErrorEnabled()){
				log.error("Data not found id: " + obj.getKey());
			}
			break;
			
		case ResultNo.DB_OPERATION_FAILED :
			if(log.isErrorEnabled()){
				log.error("DB operation failed id: " + obj.getKey());
			}
			break;
		}

	}
	
	/**
	 * callback
	 */
	public void createPlayerDataCallback(BaseDataInfo obj, Object... userObject){
	
		switch(obj.getResultCode()){
		
		case ResultNo.OK:
		{
			this.putData(obj.getKey(), obj.getHandle(), (PlayerData) obj);
			break;
		}
			
		case ResultNo.ACCESS_DENY:
			if(log.isErrorEnabled()){
				log.error("Access deny  id: " + obj.getKey());
			}
			break;
			
		case ResultNo.FAILED:
			if(log.isErrorEnabled()){
				log.error("Create failed  id: " + obj.getKey());
			}
			break;
			
		case ResultNo.DB_OPERATION_FAILED:
			if(log.isErrorEnabled()){
				log.error("DB Create failed  id: " + obj.getKey());
			}
			break;
		}
	}
	
	/**
	 * dc 變更玩家金錢返回後的預處理 callback
	 * <p>
	 * from
	 * {@link #updatePlayerMoneyToDC(Player, ChangeCoinLister, MoneyChangeWay, String, String, Object, String, Object...)
	 * updatePlayerMoneyToDC}
	 */
	public void updatePlayerMoneyToDcPreProcCallback(BaseDataInfo obj, Object... userObject) {
		MoneyChangeParam param = (MoneyChangeParam) obj.getData();
		Player player = getPlayerFromZone(param.getSrcModuleId(), param.getPlayerId());
		if (player == null) {
			return;
		}
		
		player.setDcQuery(false);

		switch (obj.getResultCode()) {
		case ResultNo.OK: {
			// 將獲得的道具放回玩家身上
			GameDropItemParam dropItemParam = param.getDropItemParam();
			if (param.getChangeWay().isDailyScore() && dropItemParam != null) {
				// 有更新的道具清單
				HashMap<String, Integer> updateItemMap = dropItemParam.getUpdateItemMap();
				for (Map.Entry<String, Integer> entry : updateItemMap.entrySet()) {
					player.setItemCount(entry.getKey(), entry.getValue());
				}

				// 新增的道具清單
				HashMap<String, Item> newItemMap = dropItemParam.getNewItemMap();
				for (Map.Entry<String, Item> entry : newItemMap.entrySet()) {
					player.updateItem(entry.getValue());
				}
				
				// 通知玩家在遊戲中獲得的道具
				ArrayList<SSimpleItem> rollItemsInGame = new ArrayList<SSimpleItem>();
				if(dropItemParam.getDropItemMap().size() > 0){
					//如果有新道具
					for (GameDropItemInfo dropItemInfo : dropItemParam.getDropItemMap().values()) {
						Item item = new Item();
						item.setItemId(dropItemInfo.getId());
						item.setCount(dropItemInfo.getCount());
						rollItemsInGame.add(item.toClientFormat());
					}
				}
				
				if(dropItemParam.isHaveCrystal()) {
					//如果有水晶
					SSimpleItem item = new SSimpleItem();
					item.setItemId(ItemSimpleData.CRYSTAL_ID);
					item.setCount(dropItemParam.getDropCrystal());
					rollItemsInGame.add(item);
				}

				if (rollItemsInGame.size() > 0) {
					RollItemSys.pushRollItemResultToClient(player.getSessionID(), rollItemsInGame);
				}
			} else if ( param.getChangeWay().isDailyBet() && dropItemParam != null && dropItemParam.isUseItemInGame()){ //遊戲中使用道具扣掉
				HashMap<String, Integer> updateItemMap = dropItemParam.getUpdateItemMap();
				for (Map.Entry<String, Integer> entry : updateItemMap.entrySet()) {
					player.setItemCount(entry.getKey(), entry.getValue());
				}
			}

			// 更新給 client
			player.sendUpdatedAttrToClient();
			
			// 送物品變化給統計
			if(player.getUserType() == PlayerUserType.PLAYER){
				sendItemChangeToAnalysis(dropItemParam);
			}
			
			sendCoinExpenditureToAnalysis(param);
			
			break;
		}

		case ResultNo.ACCESS_DENY:
			if (log.isErrorEnabled()) {
				log.error("Access deny  id: " + obj.getKey());
			}
			break;

		case ResultNo.FAILED:
			if (log.isErrorEnabled()) {
				log.error("Create failed  id: " + obj.getKey());
			}
			break;

		case ResultNo.DB_OPERATION_FAILED:
			if (log.isErrorEnabled()) {
				log.error("DB update money failed  id: " + obj.getKey());
			}
			break;
		}
		
		
		if(log.isInfoEnabled() && ServerProperties.isSaveConsoleLog(player.getUserType()))
		{
			log.info("After Update Player(" +player.toString()+ ")    Gold = " + player.getGoldCoin() + "  Lucky = " + player.getLuckyCoin() + "  Trial = " + player.getTrialCoin() );
		}
		
	}
	
	/**
	 * 更新玩家屬性及同步資料至client
	 * @param obj
	 * @param player
	 */
	public boolean UpdateAttributeAndSendToClient(BaseDataInfo obj , Player player , GUID sessionid)
	{
		MoneyChangeParam param = (MoneyChangeParam) obj.getData();
		player.setSessionID(sessionid);
		switch (obj.getResultCode()) 
		{
			case ResultNo.OK: 
			{
				// 將 dc 處理期間的數值變更套回 player data
				UpdateAttributeList.applyUpdateListToPlayer(param.getUpdatedAttrList(), player);
				
				player.sendUpdatedAttrToClient();

				String zoneId = PlayerCommonInfoManager.getInstance().getPlayerZoneId(player.getGuid());
				
				if(zoneId != null){
					SUpdatePlayerZoneDataByAttributeList msg = new SUpdatePlayerZoneDataByAttributeList();
					msg.setUpdatedAttrList(param.getUpdatedAttrList());
					msg.setPlayerSessionId(sessionid);
					
					com.xpec.c4.service.group.transportlayer.GroupMessage message = new com.xpec.c4.service.group.transportlayer.GroupMessage();
					message.setMessageType(MessageTypes.S_ZONE_UPDATE_PLAYER_ATTRIBUTE_DATA);
					message.setMessageContent(msg);
					message.setDestModuleID(zoneId);
					MessageAction.sendGroupMessage(message);
				}
				return true;
			}
		}
		return false;
	}
	
	
	/**
	 * dc 變更玩家道具返回後的預處理 callback
	 * <p>
	 * from
	 * {@link #updatePlayerItemToDC(GUID, Player, ChangeItemLister, ItemChangeWay, String, String, Object, String, Object...)
	 * updatePlayerItemToDC}
	 */
	public void updatePlayerItemToDcPreProcCallback(BaseDataInfo obj, Object... userObject) {
		ItemChangeParam param = (ItemChangeParam) obj.getData();
		Player player = getPlayerFromZone(param.getSrcModuleId(), param.getPlayerId());
		if (player != null) {
			player.setDcQuery(false);
		}

		switch (obj.getResultCode()) {
		case ResultNo.OK: {
			ChangeItemLister itemLister = param.getChangeItemLister();
			if(player == null){
				break;
			}

			int totalChangeCount = 0;
			
			// 將變更後的值套回玩家身上
			Iterator<Map.Entry<String, ChangeItem>> it = itemLister.getListIterator();
			ChangeItem itemInfo = itemLister.getNextItem(it);
			while (itemInfo != null) {
				if (GameUtility.isHaveString(itemInfo.getItemSn())) {
					player.setItemCount(itemInfo.getItemSn(), itemInfo.getNewCount());
				}
				totalChangeCount += itemInfo.getChangeCount();
				
				itemInfo = itemLister.getNextItem(it);
			}

			// 將異動過程中新增的道具填入
			if (param.getNewItemMap() != null) {
				for (Map.Entry<String, Item> entry : param.getNewItemMap().entrySet()) {
					Item item = entry.getValue();
					player.addItem(item);
				}
			}

			//有更新水晶或是角色經驗等級，用這個套到角色身上
			UpdateAttributeList.applyUpdateListToPlayer(param.getUpdatedAttrList(), player);

			
			player.sendUpdatedAttrToClient();
			
			sendItemChangeToAnalysis(param);
			break;
		}

		case ResultNo.ACCESS_DENY:
			if (log.isErrorEnabled()) {
				log.error("Access deny  id: " + obj.getKey());
			}
			break;

		case ResultNo.FAILED:
			if (log.isErrorEnabled()) {
				log.error("Create failed  id: " + obj.getKey());
			}
			break;

		case ResultNo.DB_OPERATION_FAILED:
			if (log.isErrorEnabled()) {
				log.error("DB update item failed  id: " + obj.getKey());
			}
			break;
		}
	}
	
	/**
	 * dc 變更玩家金錢返回後的預處理 callback
	 * <p>
	 * from
	 * {@link #updatePlayerMoneyAndItemToDC
	 * updatePlayerMoneyItemToDC}
	 */
	public void updatePlayerMoneyItemToDcPreProcCallback(BaseDataInfo obj, Object... userObject) {
		MoneyItemChangeParam params = (MoneyItemChangeParam) obj.getData();
		Player player = getPlayerFromZone(params.getSrcModuleId(), params.getPlayerId());
		baseUpdatePlayerMoneyItemToDcPreProcCallback(obj, params, player ,false);
	}
	
	/**
	 * 為了網頁虛寶交易產生的 
	 * 變更玩家道具返回後的預處理 callback
	 * @param obj
	 * @param userObject
	 */
	public static void webUpdatePlayerMoneyItemToDcPreProcCallback(BaseDataInfo obj, Object... userObject) {
		MoneyItemChangeParam param = (MoneyItemChangeParam) obj.getData();
		
		Player player = (Player)userObject[0];
		
		String zoneId = PlayerCommonInfoManager.getInstance().getPlayerZoneId(player.getGuid());

		if (zoneId != null && ServerProperties.isTargetModule(zoneId, ModuleName.GAMELOBBY)) {
			player.setZoneId(zoneId);
			baseUpdatePlayerMoneyItemToDcPreProcCallback(obj, param, player , true);
		}
	}

	
	private static void baseUpdatePlayerMoneyItemToDcPreProcCallback(BaseDataInfo obj, MoneyItemChangeParam params, Player player , boolean isFromWeb) {
		switch (obj.getResultCode()) {
		case ResultNo.OK: {
			if(player == null){
				if(log.isErrorEnabled()){
					
					String playerId = params.getPlayerId().toString();
					String playerName = params.getPlayerName();
					String orderId = params.getOrderId().toString();
					
					log.error(GameUtility.getCurMethodName() + " player = null, playerId = " + playerId + ", playerName = " + playerName + ", orderId = " + orderId);
				}
				break;
			}
			
			player.setDcQuery(false);
			MoneyChangeParam moneyParam = params.getMoneyParam();
			ChangeCoinLister coinLister = moneyParam.getChangeCoinLister();
			if(coinLister != null){
				if(coinLister.getSize() != 0){
					// 將變更後的錢套回玩家身上
					Iterator<Map.Entry<CoinType, ChangeCoin>> moneyit = coinLister.getListIterator();
					ChangeCoin coinInfo = coinLister.getNextCoin(moneyit);
					while (coinInfo != null) {
						player.setCoin(coinInfo.getCoinType(), coinInfo.getNewValue());
						coinInfo = coinLister.getNextCoin(moneyit);
					}
				}
			}
			
			//將屬性清單更新至player
			if(!isFromWeb){
				if(moneyParam != null){
					UpdateAttributeList.applyUpdateListToPlayer(moneyParam.getUpdatedAttrList(), player);
				}
			}
			
			ItemChangeParam itemParam = params.getItemParam();
			ChangeItemLister itemLister = itemParam.getChangeItemLister();
			
			int totalChangeCount = 0;
			
			if(itemLister != null){
				if(itemLister.getSize() != 0){
					// 將變更後的道具套回玩家身上
					Iterator<Map.Entry<String, ChangeItem>> itemit = itemLister.getListIterator();
					ChangeItem itemInfo = itemLister.getNextItem(itemit);
					while (itemInfo != null) {
						if (GameUtility.isHaveString(itemInfo.getItemSn())) {
							player.setItemCount(itemInfo.getItemSn(), itemInfo.getNewCount());
						}
						totalChangeCount += itemInfo.getChangeCount();
						
						itemInfo = itemLister.getNextItem(itemit);
					}
				}
			}
			
			// 將異動過程中新增的道具填入
			if (itemParam.getNewItemMap() != null) {
				for (Map.Entry<String, Item> entry : itemParam.getNewItemMap().entrySet()) {
					Item item = entry.getValue();
					player.addItem(item);
				}
			}
			
			if (isFromWeb) {
				// 網頁虛寶交易 送到basiczone去處理
				ArrayList<UpdateAttribute> updateAttributeLists = new ArrayList<UpdateAttribute>();

				UpdateAttributeList updateAttributeList = new UpdateAttributeList();
				updateAttributeList.addUpdateValue(PlayerAttributeType.GOLD_COIN, player.getGoldCoin());
				updateAttributeLists = updateAttributeList.getUpdateList();

				UpdateItemList updateItemList = new UpdateItemList();

				for (Item item : player.getItemBox().getItems()) {
					updateItemList.addUpdateItem(item);
				}

				SUpdatePlayerZoneDataByAttributeList msg = new SUpdatePlayerZoneDataByAttributeList();
				msg.setUpdatedAttrList(updateAttributeLists);
				msg.setUpdateItemList(updateItemList);
				msg.setPlayerSessionId(player.getSessionID());

				com.xpec.c4.service.group.transportlayer.GroupMessage message = new com.xpec.c4.service.group.transportlayer.GroupMessage();
				message.setMessageType(MessageTypes.S_ZONE_UPDATE_PLAYER_ATTRIBUTE_DATA);
				message.setMessageContent(msg);
				message.setDestModuleID(player.getZoneId());
				MessageAction.sendGroupMessage(message);
			}

			player.sendUpdatedAttrToClient();
			
			sendItemChangeToAnalysis(itemParam);
			sendCoinExpenditureToAnalysis(moneyParam);
			break;
		}

		case ResultNo.ACCESS_DENY:
			if (log.isErrorEnabled()) {
				log.error("Access deny  id: " + obj.getKey());
			}
			break;

		case ResultNo.FAILED:
			if (log.isErrorEnabled()) {
				log.error("Create failed  id: " + obj.getKey());
			}
			break;

		case ResultNo.DB_OPERATION_FAILED:
			if (log.isErrorEnabled()) {
				log.error("DB update money failed  id: " + obj.getKey());
			}
			break;
		}
	}
	
	/**
	 * 送物品變化給統計(itemChangeParam)
	 */
	private static void sendItemChangeToAnalysis(ItemChangeParam param){
		if(param == null)
			return;
		
		AnalysisGroupProcessor.sendItemChangeToAnalysis(param);
	}
	
	/**
	 * 送物品變化給統計(掉落物品)
	 */
	private static void sendItemChangeToAnalysis(GameDropItemParam dropItemParam){
		if(dropItemParam == null)
			return;
		
		//key: itemID
		HashMap<String, GameDropItemInfo> itemMap = dropItemParam.getDropItemMap();
		
		//提供給analysis的內容
		HashMap<String, Integer> changeItems = new HashMap<String, Integer>();
		
		if(itemMap != null){
			//遊戲中使用物品的統計不在這邊接，因為這邊沒辦法知道道具是精煉多少
			if(dropItemParam.isUseItemInGame()){
				return;
			}
			
			try {
				//掉寶獲得
				ItemChangeWay itemChangeWay = ItemChangeWay.ADD_IN_GAME;
				//如果有掉水晶
				if(dropItemParam.isHaveCrystal()){
					String itemIDWithRefineLevel = AnalysisGroupProcessor.getItemIDWithRefineLV(ItemCrystalCard.basicUnitItemID, 0);
					int crystalCount = dropItemParam.getDropCrystal();
					changeItems.put(itemIDWithRefineLevel, crystalCount);
				}
				
				
				for(Entry<String, GameDropItemInfo> entry : itemMap.entrySet()){
					String itemID = entry.getKey();
					int changeValue = entry.getValue().getCount();
					
					if(changeValue == 0)
						continue;
					
					ItemBasic itemBasic = ItemInfoManager.getInstance().getItem(itemID);
					if(itemBasic == null)
						continue;
					
					int refineLevel = itemBasic.getItemRefineLevel();

					String itemIDWithRefineLevel = AnalysisGroupProcessor.getItemIDWithRefineLV(itemID, refineLevel);
					if(changeItems.get(itemIDWithRefineLevel) == null){
						changeItems.put(itemIDWithRefineLevel, changeValue);
					}
					else{
						int count = changeItems.get(itemIDWithRefineLevel);
						changeItems.put(itemIDWithRefineLevel, count + changeValue);
					}
				}
				
				AnalysisGroupProcessor.sendItemChangeToAnalysis(itemChangeWay, changeItems);
			} catch (Exception e) {
				if(log.isErrorEnabled()){
					log.error("sendItemChangeToAnalysis fail", e);
				}
			}

		}
	}
	
	/**
	 * 送C幣統計
	 */
	private static void sendCoinExpenditureToAnalysis(MoneyChangeParam param){
		if(param.getChangeWay() == null || param.getUserType() != PlayerUserType.PLAYER){
			return;
		}
		
		if(param.getChangeWay().equals(MoneyChangeWay.ADD_USE_GOLDCOIN_CARD)){
			ChangeCoinLister coinLister = param.getChangeCoinLister();
			if(coinLister == null){
				return;
			}
			
			ChangeCoin changeCoin = coinLister.getCoinMap().get(CoinType.GOLD);
			if(changeCoin == null || Helper.isLessEqual(changeCoin.getChangeValue(), BigDecimal.ZERO)){
				return;
			}
			
			AnalysisGroupProcessor.sendCoinExpenditureToAnalysis(CoinExpenditureWay.GOLD_COIN_CARD, changeCoin.getChangeValue());
		}
	}
}
